﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using GolfLeague.EditModels;
using GolfLeague.ViewModels;

namespace GolfLeague.Models
{
    public class ScoreInterface
    {
        GolfLeagueEntities golfDB = new GolfLeagueEntities();

        #region Strokes

        public IQueryable<Stroke> GetMatchStrokes(int matchID)
        {
            return from strokeEntity in golfDB.Strokes
                   where strokeEntity.MatchID.Equals(matchID)
                   orderby strokeEntity.HoleID
                   select strokeEntity;
        }

        public void AddStroke(Stroke stroke)
        {
            golfDB.Strokes.AddObject(stroke);
        }

        public void DeleteStroke(Stroke stroke)
        {
            golfDB.Strokes.DeleteObject(stroke);
        }

        #endregion

        #region Scores

        public IQueryable<PlayerMatch> GetMatchScores(int matchID)
        {
            return from scoreEntity in golfDB.PlayerMatches
                   where scoreEntity.MatchID.Equals(matchID)
                   select scoreEntity;
        }

        public PlayerMatch GetPlayerMatchScore(int playerMatchID)
        {
            return (from scoreEntity in golfDB.PlayerMatches
                    where scoreEntity.PlayerMatchID.Equals(playerMatchID)
                    select scoreEntity).FirstOrDefault();
        }

        public PlayerMatch GetPlayerMatchScore(int playerID, int matchID)
        {
            return (from scoreEntity in golfDB.PlayerMatches
                    where scoreEntity.PlayerID.Equals(playerID) && scoreEntity.MatchID.Equals(matchID)
                    select scoreEntity).FirstOrDefault();
        }

        public void AddScore(PlayerMatch playerMatch)
        {
            golfDB.PlayerMatches.AddObject(playerMatch);
        }

        public void DeleteScore(PlayerMatch playerMatch)
        {
            golfDB.PlayerMatches.DeleteObject(playerMatch);
        }

        #endregion

        #region Team Points

        public IQueryable<TeamMatch> GetMatchTeamMatches(int matchID)
        {
            return from scoreEntity in golfDB.TeamMatches
                   orderby scoreEntity.TeamID
                   where scoreEntity.MatchID.Equals(matchID)
                   select scoreEntity;
        }

        public TeamMatch GetTeamPoints(int teamPointID)
        {
            return (from scoreEntity in golfDB.TeamMatches
                    where scoreEntity.TeamMatchID.Equals(teamPointID)
                    select scoreEntity).FirstOrDefault();
        }

        #endregion

        #region ScoringMethods

        public IQueryable<ScoringMethod> GetAllScoringMethods()
        {
            return from scoreEntity in golfDB.ScoringMethods
                   orderby scoreEntity.ScoringMethodName
                   select scoreEntity;
        }

        public ScoringMethod GetScoringMethod(int scoringMethodID)
        {
            return (from scoreEntity in golfDB.ScoringMethods
                    where scoreEntity.ScoringMethodID.Equals(scoringMethodID)
                    select scoreEntity).FirstOrDefault();
        }

        public void AddScoringMethod(ScoringMethod scoringMethod)
        {
            golfDB.ScoringMethods.AddObject(scoringMethod);
        }

        public void DeleteScoringMethod(ScoringMethod scoringMethod)
        {
            golfDB.ScoringMethods.DeleteObject(scoringMethod);
        }

        #endregion

        #region ScoringTypes

        public IQueryable<ScoringType> GetAllScoringTypes()
        {
            return from scoreEntity in golfDB.ScoringTypes
                   orderby scoreEntity.ScoringTypeName
                   select scoreEntity;
        }

        public ScoringType GetScoringType(int scoringTypeID)
        {
            return (from scoreEntity in golfDB.ScoringTypes
                    where scoreEntity.ScoringTypeID.Equals(scoringTypeID)
                    select scoreEntity).FirstOrDefault();
        }

        public void AddScoringType(ScoringType scoringType)
        {
            golfDB.ScoringTypes.AddObject(scoringType);
        }

        public void DeleteScoringType(ScoringType scoringType)
        {
            golfDB.ScoringTypes.DeleteObject(scoringType);
        }

        #endregion

        #region ScoringCalculations

        public IQueryable<ScoringCalculation> GetAllScoringCalculations()
        { 
            return from scoreEntity in golfDB.ScoringCalculations
                   orderby scoreEntity.CalculationName
                   select scoreEntity;
        }

        public ScoringCalculation GetScoringCalculation(int scoringCalculationID)
        {
            return (from scoreEntity in golfDB.ScoringCalculations
                    where scoreEntity.ScoringCalulationID.Equals(scoringCalculationID)
                    select scoreEntity).FirstOrDefault();
        }

        public void AddScoringCalculation(ScoringCalculation scoringCalculation)
        {
            golfDB.ScoringCalculations.AddObject(scoringCalculation);
        }

        public void DeleteScoringCalculation(ScoringCalculation scoringCalculation)
        {
            golfDB.ScoringCalculations.DeleteObject(scoringCalculation);
        }

        #endregion

        #region ScoringVariables

        public IEnumerable<ScoringVariable> GetAllScoringVariables()
        {
            return from variableEntity in golfDB.ScoringVariables
                   orderby variableEntity.DisplayName
                   select variableEntity;
        }

        public ScoringVariable GetScoringVariable(int scoringVariableID)
        {
            return (from variableEntity in golfDB.ScoringVariables
                    where variableEntity.ScoringVariableID.Equals(scoringVariableID)
                    select variableEntity).FirstOrDefault();
        }

        public void AddScoringVariable(ScoringVariable scoringVariable)
        {
            golfDB.ScoringVariables.AddObject(scoringVariable);
        }

        public void DeleteScoringVariable(ScoringVariable scoringVariable)
        {
            golfDB.ScoringVariables.DeleteObject(scoringVariable);
        }

        #endregion

        public void Save()
        {
            golfDB.SaveChanges();
        }

        #region Edit/View Model Builders

        public ScoresEditModel GetScoresEditModel(int? id)
        {
            MatchInterface matchInterface = new MatchInterface();
            RoundInterface roundInterface = new RoundInterface();
            ScheduleInterface scheduleInterface = new ScheduleInterface();
            IEnumerable<Schedule> schedules = scheduleInterface.GetAllSchedules();
            int scheduleID;
            int roundID = 0;
            List<RoundsDropDownList> roundsDropDownList = new List<RoundsDropDownList>();
            IEnumerable<Match> matches;

            if (id == null)
                if (schedules.FirstOrDefault(s => s.ScheduleYear == (short)DateTime.Now.Year) != null)
                    scheduleID = schedules.FirstOrDefault(s => s.ScheduleYear == (short)DateTime.Now.Year).ScheduleID;
                else
                    scheduleID = (schedules.Count() > 0 ? schedules.FirstOrDefault().ScheduleID : 0);
            else
            {
                roundID = Convert.ToInt32(id);
                scheduleID = roundInterface.GetRound(roundID).ScheduleID;
            }

            foreach (Round round in roundInterface.GetAllScheduleRounds(scheduleID))
                roundsDropDownList.Add(new RoundsDropDownList
                {
                    RoundID = round.RoundID,
                    RoundDate = round.RoundDate.ToShortDateString()
                });

            if (roundsDropDownList.Count > 0)
            {
                roundID = roundsDropDownList.FirstOrDefault().RoundID;
                matches = matchInterface.GetRoundMatches(roundID);
            }
            else
            {
                matches = matchInterface.GetRoundMatches(0);
                roundsDropDownList.Add(new RoundsDropDownList
                {
                    RoundID = 0,
                    RoundDate = "No rounds exist for this schedule."
                });
            }

            return new ScoresEditModel
            {
                Schedules = schedules,
                RoundsDropDownList = roundsDropDownList,
                Round = roundInterface.GetRoundViewModel(roundID),
                ScheduleID = scheduleID,
                RoundID = roundID,
                ScoresScorecardEditModel = new ScoresScorecardEditModel()
            };
        }

        public ScoresScorecardEditModel GetScoresScorecardEditModel(int matchID)
        {
            HandicapInterface handicapInterface = new HandicapInterface();
            MatchInterface matchInterface = new MatchInterface();
            PlayerInterface playerInterface = new PlayerInterface();
            ScoreCardInterface scoreCardInterface = new ScoreCardInterface();

            Match match = matchInterface.GetMatch(matchID);
            List<StandardDropdown> playerDropdown = new List<StandardDropdown>();
            List<PlayerScorecardEditModel> playerScorecardEditModels = new List<PlayerScorecardEditModel>();
            List<PlayerStroke> masterPlayerStrokes = new List<PlayerStroke>();

            IEnumerable<Hole> holes;
            if ((CourseSides)match.Round.SideID == CourseSides.Front)
            {
                holes = scoreCardInterface.GetFrontCourseHoles(match.Round.CourseID);
            }
            else
            {
                holes = scoreCardInterface.GetBackCourseHoles(match.Round.CourseID);
            }

            foreach (Hole hole in holes)
                masterPlayerStrokes.Add(new PlayerStroke
                {
                    HoleID = hole.HoleID
                });

            foreach (Player player in playerInterface.GetAllPlayers())
                playerDropdown.Add(new StandardDropdown
                {
                    dropdownID = player.PlayerID,
                    dropdownText = player.FirstName.Substring(0, 1) + ". " + player.LastName
                });

            foreach (PlayerMatch playerMatch in matchInterface.GetMatchPlayerMatches(matchID))
            {
                List<PlayerStroke> playerStrokes = new List<PlayerStroke>();
                foreach (PlayerStroke playerStroke in masterPlayerStrokes)
                    playerStrokes.Add(new PlayerStroke
                    {
                        HoleID = playerStroke.HoleID
                    });
                IEnumerable<Stroke> strokeList = GetMatchStrokes(playerMatch.MatchID);
                if (strokeList.Count() > 0)
                {
                    foreach (Stroke stroke in strokeList.Where(p => p.PlayerID == playerMatch.PlayerID))
                    {
                        playerStrokes.Single(h => h.HoleID == stroke.HoleID).Strokes = stroke.Strokes;
                    }
                }

                CourseSides courseSide = (CourseSides)match.Round.SideID;
                int playerHandicap = handicapInterface.GetCurrentHandicap(playerMatch.PlayerID).Handicap;

                if (courseSide != CourseSides.Both)
                {
                    int frontPar = scoreCardInterface.GetFrontHoleDetails(playerMatch.Match.Round.TeeBoxID).Sum(h => h.Par);
                    int backPar = scoreCardInterface.GetBackHoleDetails(playerMatch.Match.Round.TeeBoxID).Sum(h => h.Par);
                    playerHandicap = scoreCardInterface.GetPlayerHandicap(playerHandicap, frontPar, backPar, courseSide);
                }

                int playerID = (playerMatch.OriginalPlayerID == null ? playerMatch.PlayerID : Convert.ToInt32(playerMatch.OriginalPlayerID));

                playerScorecardEditModels.Add(new PlayerScorecardEditModel
                {
                    MatchHandicap = playerHandicap,
                    PlayerMatch = playerMatch,
                    PlayerID = playerMatch.PlayerID,
                    PlayerStrokes = playerStrokes.ToList(),
                    TeamID = matchInterface.GetPlayerMatchTeam(playerMatch.MatchID, playerID).TeamID
                });
            }

            IEnumerable<Team> teams = matchInterface.GetMatchTeams(matchID);
            IEnumerable<TeamMatch> teamPoints = matchInterface.GetMatchTeamMatches(matchID);
            IEnumerable<PlayerMatch> playerMatches = matchInterface.GetMatchPlayerMatches(matchID);
            decimal team1Points = teamPoints.First().Points;
            decimal team2Points = teamPoints.Last().Points;

            foreach (PlayerMatch playerMatch in playerMatches)
                if (playerMatch.Player.PlayerTeams.Count(t => t.TeamID == teams.First().TeamID) > 0)
                    team1Points += (decimal)playerMatch.Points;
                else
                    team2Points += (decimal)playerMatch.Points;

            return new ScoresScorecardEditModel
            {
                MatchID = matchID,
                PlayerDropdown = playerDropdown,
                PlayerScores = playerScorecardEditModels,
                ScoreCard = scoreCardInterface.GetScoresScorecardViewModel(match.Round.CourseID, match.Round.TeeBoxID),
                TeamMatches = new List<TeamPoints>() { new TeamPoints { TeamID = teams.First().TeamID, Points = team1Points }, new TeamPoints { TeamID = teams.Last().TeamID, Points = team2Points } }
            };
        }

        public ScoringMethodEditModel GetScoringMethodEditModel()
        {
            return GetScoringMethodEditModel(null);
        }

        public ScoringMethodEditModel GetScoringMethodEditModel(int? scoringMethodID)
        {
            List<StandardDropdown> scoringTypeDropdown = new List<StandardDropdown>();
            List<StandardDropdown> scoringCalculationDropdown = new List<StandardDropdown>();

            foreach (ScoringType scoringType in GetAllScoringTypes())
                scoringTypeDropdown.Add(new StandardDropdown
                {
                    dropdownID = scoringType.ScoringTypeID,
                    dropdownText = scoringType.ScoringTypeName
                });

            if (scoringTypeDropdown.Count == 0) scoringTypeDropdown.Add(new StandardDropdown
            {
                dropdownID = 0,
                dropdownText = "No scoring types found."
            });

            foreach (ScoringCalculation scoringCalculation in GetAllScoringCalculations())
                scoringCalculationDropdown.Add(new StandardDropdown
                {
                    dropdownID = scoringCalculation.ScoringCalulationID,
                    dropdownText = scoringCalculation.CalculationName
                });

            if (scoringCalculationDropdown.Count == 0) scoringCalculationDropdown.Add(new StandardDropdown
            {
                dropdownID = 0,
                dropdownText = "No scoring calculations found."
            });

            return new ScoringMethodEditModel
            {
                ScoringMethods = GetAllScoringMethods(),
                ScoringTypeDropdown = scoringTypeDropdown,
                ScoringCalcuationDropdown = scoringCalculationDropdown,
                NewScoringMethod = (scoringMethodID == null ? new ScoringMethod() : GetScoringMethod(Convert.ToInt32(scoringMethodID)))
            };
        }

        #endregion

        #region Helpers

        public bool SaveMatchScores(ScoresScorecardEditModel scoresScorecardEditModel)
        {
            MatchInterface matchInterface = new MatchInterface();
            ScoreCardInterface scoreCardInterface = new ScoreCardInterface();

            bool ScoreUpdates = false;

            foreach (PlayerScorecardEditModel playerScoreEditModel in scoresScorecardEditModel.PlayerScores)
            {
                PlayerMatch playerMatch = matchInterface.GetMatchPlayerMatches(scoresScorecardEditModel.MatchID).SingleOrDefault(p => p.PlayerID == playerScoreEditModel.PlayerID);
                if (playerMatch == null)
                {
                    HandicapInterface handicapInterface = new HandicapInterface();
                    TeamInterface teamInterface = new TeamInterface();
                    playerMatch = matchInterface.GetPlayerMatch(playerScoreEditModel.PlayerMatch.PlayerMatchID);
                    playerMatch.OriginalPlayerID = playerMatch.PlayerID;
                    playerMatch.PlayerID = playerScoreEditModel.PlayerID;
                    playerMatch.HandicapID = handicapInterface.GetCurrentHandicap(playerScoreEditModel.PlayerID).HandicapID;
                    PlayerMatch opponentPlayer = matchInterface.GetMatchPlayerMatches(scoresScorecardEditModel.MatchID).SingleOrDefault(p => p.PlayerID == playerMatch.OpponentID);
                    opponentPlayer.OpponentID = playerScoreEditModel.PlayerID;
                    matchInterface.Save();
                    playerScoreEditModel.PlayerMatch = playerMatch;
                }
                IEnumerable<Stroke> strokes = GetMatchStrokes(playerMatch.MatchID).Where(p => p.PlayerID == playerScoreEditModel.PlayerID);
                IEnumerable<HandicapHoleStrokes> handicapStrokes = scoreCardInterface.GetHandicapStrokesForNet((CourseSides)playerMatch.Match.Round.SideID, playerMatch);

                if (playerScoreEditModel.PlayerStrokes.Sum(s => s.Strokes) > 0)
                {
                    foreach (PlayerStroke stroke in playerScoreEditModel.PlayerStrokes)
                    {
                        int handicapStroke = handicapStrokes.SingleOrDefault(s => s.HoleNumber == scoreCardInterface.GetHole(stroke.HoleID).HoleNumber).Strokes;
                        if (strokes.SingleOrDefault(s => s.HoleID == stroke.HoleID) != null)
                        {
                            ScoreUpdates = true;
                            if (strokes.Single(s => s.HoleID == stroke.HoleID).Strokes != stroke.Strokes) strokes.Single(s => s.HoleID == stroke.HoleID).Strokes = stroke.Strokes;
                            if (strokes.Single(s => s.HoleID == stroke.HoleID).NetStrokes != (stroke.Strokes - handicapStroke)) strokes.Single(s => s.HoleID == stroke.HoleID).NetStrokes = stroke.Strokes - handicapStroke;
                        }
                        else
                            AddStroke(new Stroke
                            {
                                Strokes = stroke.Strokes,
                                HoleID = stroke.HoleID,
                                PlayerID = playerScoreEditModel.PlayerID,
                                MatchID = playerScoreEditModel.PlayerMatch.MatchID,
                                NetStrokes = stroke.Strokes - handicapStroke
                            });
                    }
                }

                //if (ScoreUpdates) TryUpdateModel(strokes);
                Save();

                PlayerMatch updatePlayerMatch = GetPlayerMatchScore(playerScoreEditModel.PlayerMatch.PlayerMatchID);
                updatePlayerMatch.GrossScore = playerScoreEditModel.PlayerStrokes.Sum(s => s.Strokes);
                updatePlayerMatch.NetScore = playerScoreEditModel.PlayerMatch.GrossScore - playerScoreEditModel.MatchHandicap;

                //TryUpdateModel(updatePlayerMatch);
                Save();
            }
            //Match match = matchInterface.GetMatch(scoresScorecardEditModel.MatchID);
            //ScoreType scoreType = (ScoreType)match.Round.RoundScoringMethods.First(r => r.RoundID == match.RoundID).ScoringMethod.ScoringTypeID;

            //if (scoreType == ScoreType.StrokePlay)
            //{
            //    // Compare NetScores for Match
            //}
            //else if (scoreType == ScoreType.MatchPlay)
            //{
            //    foreach (PlayerMatch playerMatch in scoresScorecardEditModel.PlayerScores)
            //    {

            //    }


            //}

            return ScoreUpdates;
        }

        public bool CalculateRoundPoints(int roundID)
        {
            bool success = false;
            MatchInterface matchInterface = new MatchInterface();

            foreach (Match match in matchInterface.GetRoundMatches(roundID))
            {
                List<PlayerMatch> playerMatches = matchInterface.GetMatchPlayerMatches(match.MatchID).ToList();
                playerMatches.ForEach(delegate(PlayerMatch playerMatch)
                {
                    success = CalculatePlayerMatchPoints(playerMatch.PlayerMatchID, playerMatches.Single(p => p.PlayerID == playerMatch.OpponentID).PlayerMatchID);
                    playerMatches.Remove(playerMatches.Single(p => p.PlayerID == playerMatch.OpponentID));
                });
            }

            return success;
        }

        private bool CalculatePlayerMatchPoints(int playerMatchID, int opponentMatchID)
        {
            ScoreCardInterface scoreCardInterface = new ScoreCardInterface();
            PlayerMatch playerA = GetPlayerMatchScore(playerMatchID);
            PlayerMatch playerB = GetPlayerMatchScore(opponentMatchID);
            int playerID = playerA.PlayerID;
            int opponentID = playerB.PlayerID;
            List<ScoreHole> holesWon = new List<ScoreHole>();

            //ScoreType scoreType = (ScoreType)playerA.Match.Round.RoundScoringMethods.First(r => r.RoundID == playerA.Match.RoundID).ScoringMethod.ScoringTypeID;
            playerA.Points = 0;
            playerB.Points = 0;

            foreach (RoundScoringMethod roundScoringMethod in playerA.Match.Round.RoundScoringMethods.Where(r => r.RoundID == playerA.Match.RoundID))
            {
                
                ScoreType scoreType = (ScoreType)roundScoringMethod.ScoringMethod.ScoringTypeID;
                if (scoreType == ScoreType.StrokePlay)
                {
                    // Compare NetScores for Match
                }
                else if (scoreType == ScoreType.MatchPlay)
                {
                    //HandicapInterface handicapInterface = new HandicapInterface();
                    IEnumerable<HandicapHoleStrokes> handicapStrokes = scoreCardInterface.GetHandicapStrokes((CourseSides)playerA.Match.Round.SideID, playerA);
                    //IEnumerable<HandicapHoleStrokes> handicapBStrokes = scoreCardInterface.GetHandicapStrokes((CourseSides)playerA.Match.Round.SideID, playerB);
                    IEnumerable<Stroke> playerAStrokes = GetMatchStrokes(playerA.MatchID).Where(p => p.PlayerID == playerID);
                    IEnumerable<Stroke> playerBStrokes;
                    if (playerAStrokes.Count() < 1)
                    {
                        playerAStrokes = GetMatchStrokes(playerB.MatchID).Where(p => p.PlayerID == opponentID);
                        playerBStrokes = GetMatchStrokes(playerA.MatchID).Where(p => p.PlayerID == playerID);
                    }
                    else
                        playerBStrokes = GetMatchStrokes(playerB.MatchID).Where(p => p.PlayerID == opponentID);

                    int netStrokesA;
                    int netStrokesB;
                    bool PlayerAGetStrokes = true;
                    if (handicapStrokes.First().PlayerID == playerB.PlayerID)
                        PlayerAGetStrokes = false;

                    foreach (Stroke playerAStroke in playerAStrokes)
                    {
                        Stroke playerBStroke = playerBStrokes.SingleOrDefault(h => h.HoleID == playerAStroke.HoleID);
                        netStrokesA = playerAStroke.Strokes;
                        netStrokesB = playerBStroke.Strokes;

                        if (PlayerAGetStrokes)
                            netStrokesA -= handicapStrokes.Single(h => h.HoleNumber == playerAStroke.Hole.HoleNumber).Strokes;
                        else
                            netStrokesB -= handicapStrokes.Single(h => h.HoleNumber == playerBStroke.Hole.HoleNumber).Strokes;

                        if (netStrokesA > netStrokesB)
                            holesWon.Add(new ScoreHole { holeID = playerBStroke.HoleID, playerID = playerBStroke.PlayerID });
                        else if (netStrokesB > netStrokesA)
                            holesWon.Add(new ScoreHole { holeID = playerAStroke.HoleID, playerID = playerAStroke.PlayerID });
                        else if (netStrokesA == netStrokesB)
                            holesWon.Add(new ScoreHole { holeID = playerAStroke.HoleID, playerID = 0 });
                    }

                }
                decimal pointsPerHoleWin = 0;
                decimal pointsPerHoleTie = 0;

                //decimal pointsPerMatchWin = 0;
                //decimal pointsPerMatchTie = 0;

                playerA.Points += holesWon.Count(p => p.playerID == playerA.PlayerID) * pointsPerHoleWin;// +(decimal)(holesWon.Count(p => p.playerID == 0) * 0.5);
                playerB.Points += holesWon.Count(p => p.playerID == playerB.PlayerID) * pointsPerHoleWin;// +(decimal)(holesWon.Count(p => p.playerID == 0) * 0.5);
                playerA.Points += holesWon.Count(p => p.playerID == 0) * pointsPerHoleTie;
                playerB.Points += holesWon.Count(p => p.playerID == 0) * pointsPerHoleTie;
            }
            Save();
            
            return true;
        }

        private class ScoreHole
        {
            public int holeID { get; set; }
            public int playerID { get; set; }
        }

        public enum ScoreType
        {
            MatchPlay = 1,
            StrokePlay = 2
        }

        #endregion
    }
}